package com.xuelang.pipeline.infrastructure.pipeline.repository;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xuelang.hiforce.base.cache.client.service.DistributeCacheManager;
import com.xuelang.hiforce.base.tracing.TracingUtils;
import com.xuelang.pipeline.client.common.constant.Constant;
import com.xuelang.pipeline.client.common.result.PageRecord;
import com.xuelang.pipeline.client.common.util.BizAssert;
import com.xuelang.pipeline.client.pipeline.dto.GroupQuery;
import com.xuelang.pipeline.domain.pipeline.Group;
import com.xuelang.pipeline.domain.pipeline.repository.GroupRepository;
import com.xuelang.pipeline.infrastructure.pipeline.converter.GroupToGroupDOConverter;
import com.xuelang.pipeline.infrastructure.pipeline.dataobject.GroupDO;
import com.xuelang.pipeline.infrastructure.pipeline.mapper.GroupMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 流水线分组仓库实现类
 *
 * @author xiwen.zxg
 * @since 2024/5/15
 */
@Slf4j
@Repository
public class GroupRepositoryImpl implements GroupRepository {

    @Autowired
    GroupMapper groupMapper;

    @Autowired
    DistributeCacheManager cacheManager;

    @Override
    public PageRecord<Group> pageList(long currentPage, long pageSize, GroupQuery groupQuery) {
        initMyFavorite();
        currentPage = currentPage < 1 ? 1 : currentPage;
        pageSize = pageSize < 1 ? 10 : pageSize;
        Page<GroupDO> page = new Page<>(currentPage, pageSize);
        LambdaQueryWrapper<GroupDO> wrapper = Wrappers.lambdaQuery(GroupDO.class);
        if (groupQuery != null) {
            if (StringUtils.isNotBlank(groupQuery.getUuid())) {
                wrapper.eq(GroupDO::getUuid, groupQuery.getUuid());
            }
            if (StringUtils.isNotBlank(groupQuery.getName())) {
                wrapper.like(GroupDO::getName, groupQuery.getName());
            }
            if (StringUtils.isNotBlank(groupQuery.getType())) {
                wrapper.eq(GroupDO::getType, groupQuery.getType());
            }
        }
        String currentUserId = TracingUtils.getUserId();
        wrapper.eq(StringUtils.isNotBlank(currentUserId), GroupDO::getCreatorId, currentUserId);
        groupMapper.selectPage(page, wrapper);
        return new PageRecord<>(
                page.getCurrent(),
                page.getSize(),
                page.getTotal(),
                page.getPages(),
                page.getRecords().stream().map(GroupToGroupDOConverter.INSTANCE::groupDOToGroup).collect(Collectors.toList())
        );
    }

    @Override
    public List<Group> list(GroupQuery groupQuery) {
        initMyFavorite();
        LambdaQueryWrapper<GroupDO> wrapper = Wrappers.lambdaQuery(GroupDO.class);
        if (groupQuery != null) {
            if (StringUtils.isNotBlank(groupQuery.getUuid())) {
                wrapper.eq(GroupDO::getUuid, groupQuery.getUuid());
            }
            if (StringUtils.isNotBlank(groupQuery.getName())) {
                wrapper.like(GroupDO::getName, groupQuery.getName());
            }
            if (StringUtils.isNotBlank(groupQuery.getType())) {
                wrapper.eq(GroupDO::getType, groupQuery.getType());
            }
            Integer isDefault = groupQuery.getIsDefault();
            wrapper.eq(Objects.nonNull(isDefault), GroupDO::getIsDefault, isDefault);
            String currentUserId = TracingUtils.getUserId();
            wrapper.eq(StringUtils.isNotBlank(currentUserId), GroupDO::getCreatorId, currentUserId);
        }

        // 按照 sort 字段升序排序
        wrapper.orderByAsc(GroupDO::getSort);

        return groupMapper.selectList(wrapper).stream()
                .map(GroupToGroupDOConverter.INSTANCE::groupDOToGroup).collect(Collectors.toList());
    }

    @Override
    public List<Group> list() {
        initMyFavorite();
        return groupMapper.selectList(null).stream()
                .map(GroupToGroupDOConverter.INSTANCE::groupDOToGroup).collect(Collectors.toList());
    }

    @Override
    public Boolean save(Group group) {
        return groupMapper.insert(GroupToGroupDOConverter.INSTANCE.groupToGroupDO(group)) == 1;
    }

    @Override
    public Boolean batchSaveOrUpdate(List<Group> groupList, String creatorId, Date createTime) {
        List<GroupDO> doList = groupList.stream()
                .map(GroupToGroupDOConverter.INSTANCE::groupToGroupDO)
                .collect(Collectors.toList());

        // 分离新增加的分组和已有的分组
        List<GroupDO> newGroups = Lists.newArrayList();
        List<GroupDO> existingGroups = Lists.newArrayList();

        for (GroupDO groupDO : doList) {
            if (StringUtils.isBlank(groupDO.getUuid())) {
                // 新增加的分组
                groupDO.setUuid(IdUtil.fastSimpleUUID());
                groupDO.setCreatorId(creatorId);
                groupDO.setCreateTime(createTime);
                newGroups.add(groupDO);
            } else {
                // 已有的分组
                groupDO.setUpdaterId(creatorId);
                groupDO.setUpdateTime(createTime);
                existingGroups.add(groupDO);
            }
        }

        // 获取当前所有分组的UUID
        List<String> currentUuids = groupMapper.selectAllUuids();
        List<String> incomingUuids = doList.stream()
                .map(GroupDO::getUuid)
                .collect(Collectors.toList());

        // 找出需要删除的旧分组UUID
        List<String> uuidsToDelete = currentUuids.stream()
                .filter(uuid -> !incomingUuids.contains(uuid))
                .collect(Collectors.toList());

        // 批量删除旧分组
        if (!uuidsToDelete.isEmpty()) {
            groupMapper.deleteByUuids(uuidsToDelete);
        }

        // 批量插入新增加的分组
        if (!newGroups.isEmpty()) {
            groupMapper.insertBatch(newGroups, creatorId, createTime);
        }

        // 批量更新已有的分组
        if (!existingGroups.isEmpty()) {
            groupMapper.updateBatch(existingGroups, creatorId, createTime);
        }

        return true;
    }

    @Override
    public Boolean update(Group group) {
        return groupMapper.update(
                GroupToGroupDOConverter.INSTANCE.groupToGroupDO(group),
                Wrappers.lambdaQuery(GroupDO.class).eq(GroupDO::getUuid, group.getUuid())) == 1;
    }

    @Override
    public Boolean delete(String uuid) {
        return groupMapper.delete(Wrappers.lambdaQuery(GroupDO.class).eq(GroupDO::getUuid, uuid)) == 1;
    }

    @Override
    public Group query(String uuid) {
        GroupDO groupDO = groupMapper.selectOne(Wrappers.lambdaQuery(GroupDO.class).eq(GroupDO::getUuid, uuid));
        return GroupToGroupDOConverter.INSTANCE.groupDOToGroup(groupDO);
    }

    @Override
    public Boolean existsName(String uuid, String name) {
        List<GroupDO> groupList =  groupMapper.selectList(Wrappers.lambdaQuery(GroupDO.class).eq(GroupDO::getName, name));
        if (ObjectUtil.isEmpty(groupList)) {
            return false;
        }
        return groupList.stream().anyMatch(groupDO -> !StringUtils.equals(uuid, groupDO.getUuid()));
    }

    @Override
    public List<Group> listByUuidList(List<String> uuidList) {
        return groupMapper.selectList(Wrappers.lambdaQuery(GroupDO.class).in(GroupDO::getUuid, uuidList))
                .stream().map(GroupToGroupDOConverter.INSTANCE::groupDOToGroup).collect(Collectors.toList());
    }

    /**
     * 初始化”我的收藏夹”，若不存在则进行初始化，若存在则不做任何处理
     */
    private void initMyFavorite(){
        String userId = TracingUtils.getUserId();
        BizAssert.notEmpty(userId, "请检查登录状态！当前无法获取用户id");

        String lockKey = Constant.PIPELINE_GROUP_MY_FAVORITE_INIT_KEY + userId;
        boolean isLocked = cacheManager.setIfAbsent(lockKey, "lock", 10L, TimeUnit.SECONDS);

        if (isLocked) {
            try {
                LambdaQueryWrapper<GroupDO> wrapper = Wrappers.lambdaQuery(GroupDO.class);
                wrapper.eq(GroupDO::getCreatorId, userId);
                wrapper.eq(GroupDO::getName, Constant.DEFAULT_MY_FAVORITE);
                GroupDO myFavorite = groupMapper.selectOne(wrapper);

                if (Objects.isNull(myFavorite)) {
                    Group initFavorite = new Group();
                    initFavorite.setUuid(IdUtil.fastSimpleUUID());
                    initFavorite.setName(Constant.DEFAULT_MY_FAVORITE);
                    initFavorite.setType("DEFAULT");
                    initFavorite.setSort(1);
                    initFavorite.setIsDefault(1);
                    save(initFavorite);
                }
            } finally {
                cacheManager.del(lockKey);
            }
        }
    }
}
